/*
 * Copyright 2011-2012 by The Regents of the University of California Licensed
 * under the Apache License, Version 2.0 (the "License"); you may not use this
 * file except in compliance with the License. you may obtain a copy of the
 * License from
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package edu.ucsd.db.bassarisk.indexer.simple_wp;

import edu.ucsd.db.bassarisk.indexer.api.IArtifactParser;
import edu.ucsd.db.bassarisk.indexer.api.IPosting.ISequencedPosting;
import edu.ucsd.db.bassarisk.indexer.api.IPostingFactory;
import edu.ucsd.db.bassarisk.indexer.api.IPostingHandler;
import edu.ucsd.db.bassarisk.indexer.api.ITextArtifact;

public class SimpleWpArticleParser implements IArtifactParser {
  private enum State {
    TITLE,
    BODY,
    PARAGRAPH,
    SENTENCE
  }

  // Magic words that appear in the stripped wikipedia articles
  private static final String SENTENCE_SEPARATOR = "\u2028";
  private static final String PARAGRAPH_SEPARATOR = "\u2029";

  // Tags used to mark generated postings
  private static final String ARTICLE_TAG = "/wp_article";
  private static final String ARTICLE_ID_TAG = "/id";
  private static final String SENTENCE_TAG = "//__sentence__";
  private static final String PARAGRAPH_TAG = "//__paragraph__";
  private static final String TITLE_TAG = "/wp_article/title";

  // Injected dependencies
  private final IPostingHandler<ISequencedPosting> handler;
  private final IPostingFactory postingFactory;

  // Keeps track of the state-machine node
  private State parserState;

  public SimpleWpArticleParser(final IPostingHandler<ISequencedPosting> handler,
                               final IPostingFactory postingFactory) {
    this.handler = handler;
    this.postingFactory = postingFactory;
  }

  @Override
  public void collectPostings(final ITextArtifact artifact) {
    SimpleWpArticle article = (SimpleWpArticle) artifact;
    // Postings at the beginning of the article
    this.handler.post(this.postingFactory.runStart(ARTICLE_TAG));
    this.handler.post(this.postingFactory.stringValuePosting(ARTICLE_TAG,
                                                             ARTICLE_ID_TAG, article
                                                                 .getWikipediaId()));
    this.handler.post(this.postingFactory.runStart(TITLE_TAG));
    this.parserState = State.TITLE;

    // Process the article content
    // We could do this using String.split, but I'm going to put minimal
    // effort into making something slightly more efficient here.
    char SPACE = ' ';
    int cursor1 = 0; // Marks the beginning of the next word
    int cursor2 = 0; // Marks the space following the next word
    String word = null;

    String content = article.getContent();
    // Advance cursor2, to first space following cursor1
    while ((cursor2 = content.indexOf(SPACE, cursor1)) > 0) {
      // Cursor1 marks the space before the word, cursor1 the space after
      word = content.substring(cursor1, cursor2);
      this.processWord(word);
      cursor1 = cursor2 + 1;
    }
    // There may be a trailing word
    word = content.substring(cursor1);
    this.processWord(word);

    // Postings at the end of the article
    switch (this.parserState) {
    case SENTENCE:
      this.handler.post(this.postingFactory.runEnd(SENTENCE_TAG));
    case PARAGRAPH:
      this.handler.post(this.postingFactory.runEnd(PARAGRAPH_TAG));
    default:
      // do nothing
    }
    this.handler.post(this.postingFactory.runEnd(ARTICLE_TAG));
  }

  /**
   * Helper method to collectPostings above.
   * 
   * Process a single word from a wex-simplified wikipedia article. Add the
   * token posting and sentence/paragraph postings as appropriate.
   * 
   * @param word
   *          The Lexical token of the word being processed
   */
  private void processWord(final String word) {
    if (word.equals(SENTENCE_SEPARATOR)) {
      switch (this.parserState) {
      case SENTENCE:
        this.handler.post(this.postingFactory.runEnd(SENTENCE_TAG));
        this.parserState = State.PARAGRAPH;
        break;
      case PARAGRAPH:
        break; // do nothing
      case BODY:
        break; // do nothing
      case TITLE:
        this.handler.post(this.postingFactory.runEnd(TITLE_TAG));
        this.parserState = State.BODY;
        break;
      }
    } else if (word.equals(PARAGRAPH_SEPARATOR)) {
      switch (this.parserState) {
      case SENTENCE:
        this.handler.post(this.postingFactory.runEnd(SENTENCE_TAG));
        this.handler.post(this.postingFactory.runEnd(PARAGRAPH_TAG));
        this.parserState = State.BODY;
        break;
      case PARAGRAPH:
        this.handler.post(this.postingFactory.runEnd(PARAGRAPH_TAG));
        this.parserState = State.BODY;
        break;
      case BODY:
        break; // do nothing
      case TITLE:
        this.handler.post(this.postingFactory.runEnd(TITLE_TAG));
        this.parserState = State.BODY;
        break;
      }
    } else if (word.length() > 0) {
      switch (this.parserState) {
      case SENTENCE:
        break; // do nothing
      case PARAGRAPH:
        this.handler.post(this.postingFactory.runStart(SENTENCE_TAG));
        this.parserState = State.SENTENCE;
        break;
      case BODY:
        this.handler.post(this.postingFactory.runStart(PARAGRAPH_TAG));
        this.handler.post(this.postingFactory.runStart(SENTENCE_TAG));
        this.parserState = State.SENTENCE;
        break;
      case TITLE:
        break; // do nothing
      }
      this.handler.post(this.postingFactory.tokenPosting(word));
    }
  }

}
